using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public partial class CharacterSheet : LoadableJSONObject{
	
	public static List<string> ValidAlignments = new List<string>
	{
		"Lawful Good", "Lawful Neutral", "Lawful Evil", "Neutral Good", "Neutral", "Neutral Evil",
		"Chaotic Good", "Chaotic Neutral", "Chaotic Evil"
	};
	
	public List<CharacterClass> classes;
	
	public string classString
	{
		get
		{
			string retStr = "";
			foreach(CharacterClass c in classes)
			{
				if(retStr.Length > 0)
					retStr = retStr + ", ";
				retStr = retStr + c.name + "(" + c.level + ")";
			}
			return retStr;
		}
	}
	
	public int classHP
	{
		get
		{
			int cHP = 0;
			foreach(CharacterClass c in classes)
			{
				cHP += c.hp;
			}
			
			return cHP;
		}
	}
	
	public int conHP
	{
		get
		{	
			return ConstitutionModifier * Level;
		}
	}
	
	public int Damage = 0;
	public int nonLethalDamage = 0;
	
	public int bonusHP = 0;
	
	public void SetBonusHP(string b)
	{
		Debug.Log("Set Bonus HP To: " + b);
		int i;
		if(int.TryParse(b, out i))
		{
			if(i < 0)
				return;
			bonusHP = i;
			Debug.Log("Set Bonus HP To: " + bonusHP);
		}
	}
	
	public void Heal(string h)
	{
		int i;
		if(int.TryParse(h, out i) && i >= 0)
		{
			Damage -= i;
			if(Damage < 0)
				Damage = 0;
		}
	}
	
	public void TakeDamage(string d)
	{
		int i;
		if(int.TryParse(d, out i) && i >= 0)
		{
			Damage += i;
		}
	}
	
	public int HP
	{
		get
		{
			int buffsBonus = GetBuffMods("Max HP").AddUpTotals();
			return Mathf.Max(1,classHP + ConstitutionModifier * Level + bonusHP + buffsBonus);
		}
	}
	
	public string hpStatus
	{
		get
		{
			float q = ((float)Damage) / ((float)HP);
			if(q == 0)
				return "Fine";
			else if(q < 0.25f)
				return "Scratched";
			else if(q < 0.5f)
				return "Injured";
			else if(q <= 0.75f)
				return "Bloodied";
			else if(q < 1)
				return "Critical";
			else
			{
				int rHP = HP - Damage;
				if(rHP == 0)
					return "Staggered";
				else if(rHP > -Constitution)
					return "Unconcious";
			}
			
			return "Dead";
		}
	}
	
	public int RandomizeHP()
	{
		int cHP = 0;
		
		foreach(CharacterClass c in classes)
		{
			cHP += c.hpRandomize;
		}
		
		return cHP;
	}
	
	public int Experience = 0;
	
	public Race race;
	public string Gender;
	
	public string Alignment;
	
	public List<WeaponItem> weapons;
	public List<Skill> skills;
	public List<InventoryItem> inventories;

	public List<InventoryItem> equippedItems
	{
		get
		{
			return inventories.FindAll(i => i.isEquipped);
		}
	}
	
	public List<InventoryItem> protectiveItems
	{
		get
		{
			return inventories.FindAll(i => i.buffs.FindAll(b => b.modifiers.FindAll(t => t.toStat == "AC").Count > 0).Count > 0);
		}
	}
	
	public string Size
	{
		get
		{
			return race.Size;
		}
	}
	
	int SizeNumeric
	{
		get
		{
			switch(Size)
			{
			case "Fine":
				return 0;
			case "Diminutive":
				return 1;
			case "Tiny":
				return 2;
			case "Small":
				return 3;
			case "Large":
				return 5;
			case "Huge":
				return 6;
			case "Gargantuan":
				return 7;
			case "Colossal":
				return 8;
			}
			
			return 4;
		}
	}
	
	public int BonusSpeed = 0;
	
	public bool SetBonusSpeed(string input)
	{
		int i; 
		if((int.TryParse(input, out i)))
		{
			if(i < 255)
			{
				BonusSpeed = i;
				return true;
			}
		}
		return false;
	}
	
	public int Speed
	{
		get
		{
			return race.Speed + BonusSpeed;
		}
	}
	
	public int ModifiedSpeed
	{
		get
		{
			if(EncumberanceNumeric > 0 && EncumberanceNumeric < 3)
				return Speed - (Mathf.FloorToInt(((float)Speed) / 15f) * 5);
			else if (EncumberanceNumeric == 3)
				return 0;
			return Speed;
		}
	}
	
	public int SizeModifier
	{
		get
		{
			return Mathf.FloorToInt(Mathf.Pow(2,Mathf.Abs(SizeNumeric-4))/2) * -((SizeNumeric-4).Sign());
		}
	}
	
	public int BAB
	{
		get
		{
			int v = 0;
			foreach(CharacterClass c in classes)
			{
				v += c.GetBAB();
			}
			
			return Mathf.Max(v,0);
		}
	}
	
	public int MeleeAB
	{
		get
		{
			int buffsBonus = GetBuffMods("Melee Attacks").AddUpTotals();
			
			return BAB + StrengthModifier + buffsBonus;
		}
	}
	
	public int RangedAB
	{
		get
		{
			int buffsBonus = GetBuffMods("Ranged Attacks").AddUpTotals();
			
			return BAB + DexterityModifier + buffsBonus;
		}
	}
	
	public int CMB
	{
		get
		{
			int f = GetBuffMods("Combat Maneuvers").AddUpTotals();
			
			return MeleeAB + -SizeModifier + f;
		}
	}
	
	public int CMD
	{
		get
		{
			return 10 + MeleeAB + -SizeModifier + DexterityModifier + GetBuffMods("CMD").AddUpTotals();
		}
	}
	
	public int BonusFort = 0;
	
	public bool SetBonusFort(string s)
	{
		int i;
		if(int.TryParse(s, out i))
		{
			if(Mathf.Abs(i) < 128)
			{
				BonusFort = i;
				return true;
			}
		}
		
		return false;
	}
	
	public int Fort
	{
		get
		{
			int f = 0;
			foreach(CharacterClass c in classes)
			{
				f += c.Fortitude;
			}
			
			f += BonusFort + ConstitutionModifier + GetBuffMods("Fortitude").AddUpTotals();
			
			return f;
		}
	}
	
	public int BonusRef = 0;
	
	public bool SetBonusRef(string s)
	{
		int i;
		if(int.TryParse(s, out i))
		{
			if(Mathf.Abs(i) < 128)
			{
				BonusRef = i;
				return true;
			}
		}
		
		return false;
	}
	
	public int Ref
	{
		get
		{
			int f = 0;
			foreach(CharacterClass c in classes)
			{
				f += c.Reflex;
			}
			
			f += BonusRef + DexterityModifier + GetBuffMods("Reflex").AddUpTotals();

			return f;
		}
	}
	
	public int BonusWill = 0;
	
	public bool SetBonusWill(string s)
	{
		int i;
		if(int.TryParse(s, out i))
		{
			if(Mathf.Abs(i) < 128)
			{
				BonusWill = i;
				return true;
			}
		}
		
		return false;
	}
	
	public int Will
	{
		get
		{
			int f = 0;
			foreach(CharacterClass c in classes)
			{
				f += c.Will;
			}
			
			f += BonusWill + WisdomModifier + GetBuffMods("Will").AddUpTotals();
			
			return f;
		}
	}
	
	public int MaxDex
	{
		get
		{
			int v = 999;
			
			foreach(BuffDebuff b in buffs)
			{
				List<TemporaryBonus> mods = b.modifiers.FindAll(m => m .toStat == "Max. Dex Bonus");
				foreach(TemporaryBonus limit in mods)
				{
					v = Mathf.Min(limit.amount, v);
				}
			}
			
			if(EncumberanceNumeric == 1)
			{
				v = Mathf.Min(v, 3);
			}
			else if(EncumberanceNumeric >= 2)
			{
				v = Mathf.Min(v, 1);
			}
			
			return v;
		}
	}
	
	public int ArmorCheckPenalty
	{
		get
		{
			int v = 0;
			
			v += GetBuffMods("Armor Check").AddUpTotals();
			
			if(EncumberanceNumeric == 1)
			{
				v += 3;
			}
			else if(EncumberanceNumeric >= 2)
			{
				v += 6;
			}
			
			return v;
		}
	}
	
	public int AC
	{
		get
		{			
			int buffsBonus = GetBuffMods("AC").AddUpTotals();
			
			return 10 + Mathf.Min(DexterityModifier, MaxDex) + buffsBonus;
		}
	}
	
	public bool Quadruped
	{
		get
		{
			return race.Quadruped;
		}
	}
	
	float SizeCarryingMod
	{
		get
		{
			switch(Size)
			{
			case "Fine":
				return 0.125f;
			case "Diminutive":
				return 0.25f;
			case "Tiny":
				return 0.5f;
			case "Small":
				return 0.75f;
			case "Large":
				return 2;
			case "Huge":
				return 4;
			case "Gargantuan":
				return 8;
			case "Colossal":
				return 16;
			}
			
			return 1;
		}
	}
	
	public int HeavyLoad
	{
		get
		{
			if(Strength < 10)
			{
				return Mathf.RoundToInt(Strength * 10 * ((Quadruped)?(1.5f):(1)) * SizeCarryingMod);
			}
			else
			{
				return Mathf.RoundToInt(100 * Mathf.Pow(4, (Strength-1)/10)  * 10 * ((Quadruped)?(1.5f):(1)) * SizeCarryingMod);
			}
		}
	}
	
	public int MediumLoad
	{
		get
		{
			return Mathf.FloorToInt((HeavyLoad * 2) / 3);
		}
	}
	
	public int LightLoad
	{
		get
		{
			return Mathf.FloorToInt(HeavyLoad / 3);
		}
	}
	
	public float CarryingWeight
	{
		get
		{
			float v = 0f;
			foreach(InventoryItem i in inventories)
			{
				v += i.weight;
			}
			foreach(WeaponItem i in weapons)
			{
				v += i.weight;
			}
			return v;
		}
	}
	
	int EncumberanceNumeric
	{
		get
		{
			float carrying = CarryingWeight;
			if(carrying > HeavyLoad)
				return 3;
			else if(carrying > MediumLoad)
				return 2;
			else if(carrying > LightLoad)
				return 1;
			else
				return 0;
		}
	}
	
	public string Encumberance
	{
		get
		{
			switch(EncumberanceNumeric)
			{
			case 1:
				return "Medium Load";
			case 2:
				return "Heavy Load";
			case 3:
				return "Over Uncumbered";
			}
			return "Light Load";
		}
	}
	
	public List<string> _classSkills;
	
	public bool IsClassSkill (string name)
	{
		if(_classSkills == null)
		{
			_classSkills = new List<string>();
			foreach(CharacterClass c in classes)
			{
				_classSkills.AddRange(c.classSkills);
			}
		}
		
		return _classSkills.Contains(name);
	}
	
	public int totalRanks
	{
		get
		{
			int i = IntelligenceModifier * Level;
			foreach(CharacterClass c in classes)
			{
				i += c.skillsPerLevel * c.level;
			}
			return i;
		}
	}
	
	public int ranksUsed
	{
		get
		{
			int i = 0;
			foreach(Skill s in skills)
			{
				i += s.rank;
			}
			
			return i;
		}
	}
	
	public int totalFeats
	{
		get
		{
			return 1 + Mathf.FloorToInt(Level / 2);
		}
	}
	
	public int featsUsed
	{
		get
		{
			return addedFeats.Count;
		}
	}
	
	static Dictionary<string,Dictionary<int,int>> expTable = new Dictionary<string, Dictionary<int, int>>
	{
		{"Fast", new Dictionary<int,int>
			{
			{0,	0},
			{1,	1300},
			{2, 3300},
			{3, 6000},
			{4, 10000},
			{5, 15000},
			{6, 23000},
			{7, 34000},
			{8, 50000},
			{9, 71000},
			{10, 105000},
			{11, 145000},
			{12, 210000},
			{13, 295000},
			{14, 425000},
			{15, 600000},
			{16, 850000},
			{17, 1200000},
			{18, 1700000},
			{19, 2400000}
			}
		},
		{"Medium", new Dictionary<int,int>
			{
			{0,	0},
			{1,	2000},
			{2, 5000},
			{3, 9000},
			{4, 15000},
			{5, 23000},
			{6, 35000},
			{7, 51000},
			{8, 75000},
			{9, 105000},
			{10, 155000},
			{11, 220000},
			{12, 315000},
			{13, 445000},
			{14, 635000},
			{15, 890000},
			{16, 1300000},
			{17, 1800000},
			{18, 2550000},
			{19, 3600000}
			}
		},
		{"Slow", new Dictionary<int,int>
			{
			{0,	0},
			{1,	3000},
			{2, 7500},
			{3, 14000},
			{4, 23000},
			{5, 35000},
			{6, 53000},
			{7, 77000},
			{8, 115000},
			{9, 160000},
			{10, 235000},
			{11, 330000},
			{12, 475000},
			{13, 665000},
			{14, 955000},
			{15, 1350000},
			{16, 1900000},
			{17, 2700000},
			{18, 3850000},
			{19, 5350000}
			}
		}
	};
	
	public int Level
	{
		get
		{
			int l = 0;
			foreach(CharacterClass c in classes)
			{
				l += c.level;
			}

			return l;
		}
	}
	
	public int bonusSpellsByLevel(int level, string stat)
	{
		float num = 0;
		
		switch(stat)
		{
		case "Intelligence":
			num = Intelligence;
			break;
		case "Wisdom":
			num = Wisdom;
			break;
		case "Charisma":
			num = Charisma;
			break;		
		}
		
		return Mathf.Max(0,Mathf.FloorToInt((num - (2f + 2f*level))/8));
	}
	
	public string progressionSpeed = "Fast";
	
	public int nextLevelExperience
	{
		get
		{
			if(Level >= 20)
				return 0;
			else
			{
				return expTable[progressionSpeed][Level];
			}
		}
	}
	
	public List<Ability> addedFeats = new List<Ability>{};
	
	public List<Ability> Feats
	{
		get
		{
			List<Ability> f = new List<Ability>();
			
			foreach(CharacterClass c in classes)
			{
				foreach(Ability a in c.Abilities)
				{
					if(a.type == "Feat")
						f.Add(a);
				}
			}
			
			foreach(Ability a in race.loadedRacialAbilities)
			{
				if(a.type == "Feat")
					f.Add(a);
			}
			
			f.AddRange(addedFeats);
			
			return f;
		}
	}
	
	public List<Ability> addedSpecialAbilities = new List<Ability>{};
	
	public List<Ability> specialAbility
	{
		get
		{
			List<Ability> sa = new List<Ability>();
			
			foreach(CharacterClass c in classes)
			{
				foreach(Ability a in c.Abilities)
				{
					if(a.type == "SpecialAbility")
						sa.Add(a);
				}
			}
			
			foreach(Ability a in race.loadedRacialAbilities)
			{
				if(a.type == "SpecialAbility")
					sa.Add(a);
			}
			
			sa.AddRange(addedSpecialAbilities);
			
			return sa;
		}
	}
	
	public int sneakDice
	{
		get
		{
			int sad = 0;
			foreach(CharacterClass c in classes)
			{
				sad += c.Abilities.FindAll(a => a.name == "Sneak Attack +1d6").Count;
			}
			
			return sad;
		}
	}

	public void AddNewBuffs (List<BuffDebuff> buffs)
	{
		if(SheetManager.activeCharacter._storedBuffs != null)
			SheetManager.activeCharacter._storedBuffs.AddRange(buffs);
		
		foreach(BuffDebuff buff in buffs)
		{
			foreach(TemporaryBonus tb in buff.modifiers)
			{
				if(SheetManager.activeCharacter.buffsDatabase.ContainsKey(tb.toStat))
					SheetManager.activeCharacter.buffsDatabase.Remove(tb.toStat);
			}
		}
	}
	
	public ExtendedCharacterStats extraInfo;
	
	public CharacterSheet()
	{
		
	}
	
	public override void Load(string json, string file)
	{
		fileName = file;
		Dictionary<string,string> data = (MiniJSON.Json.Deserialize(json) as Dictionary<string,object>).ToDSS();
		
		name = data["name"];
		
		race = new Race();
		race.Load(data["race"], "");
		
		description = data["description"];
		
		_Strength = int.Parse(data["_Strength"]);
		_Dexterity = int.Parse(data["_Dexterity"]);
		_Constitution = int.Parse(data["_Constitution"]);
		_Intelligence = int.Parse(data["_Intelligence"]);
		_Wisdom = int.Parse(data["_Wisdom"]);
		_Charisma = int.Parse(data["_Charisma"]);
		
		BonusSpeed = int.Parse(data["BonusSpeed"]);
		
		BonusFort = int.Parse(data["BonusFort"]);
		BonusRef = int.Parse(data["BonusRef"]);
		BonusWill = int.Parse(data["BonusWill"]);
		
		classes = new List<CharacterClass>();
		List<object> classesJson = MiniJSON.Json.Deserialize(data["classes"]) as List<object>;
		foreach(string classJson in classesJson)
		{
			CharacterClass c = new CharacterClass();
			c.Load(classJson, "");
			classes.Add(c);
		}
		
		Damage = int.Parse(data["Damage"]);
		bonusHP = int.Parse(data["bonusHP"]);
		Experience = int.Parse(data["Experience"]);
		
		Gender = data["Gender"];
		
		Alignment = data["Alignment"];
		
		weapons = new List<WeaponItem>();
		List<object> objectsJson = MiniJSON.Json.Deserialize(data["weapons"]) as List<object>;
		foreach(string objJson in objectsJson)
		{
			WeaponItem o = new WeaponItem();
			o.Load(objJson, "");
			weapons.Add(o);
		}
		
		skills = new List<Skill>();
		objectsJson = MiniJSON.Json.Deserialize(data["skills"]) as List<object>;
		foreach(string objJson in objectsJson)
		{
			Skill o = new Skill();
			o.Load(objJson, "");
			skills.Add(o);
		}
		
		addedFeats = new List<Ability>();
		objectsJson = MiniJSON.Json.Deserialize(data["addedFeats"]) as List<object>;
		foreach(string objJson in objectsJson)
		{
			Ability o = new Ability();
			o.Load(objJson, "");
			addedFeats.Add(o);
		}
		
		addedSpecialAbilities = new List<Ability>();
		objectsJson = MiniJSON.Json.Deserialize(data["addedSpecialAbilities"]) as List<object>;
		foreach(string objJson in objectsJson)
		{
			Ability o = new Ability();
			o.Load(objJson, "");
			addedSpecialAbilities.Add(o);
		}
		
		inventories = new List<InventoryItem>();
		objectsJson = MiniJSON.Json.Deserialize(data["inventories"]) as List<object>;
		foreach(string objJson in objectsJson)
		{
			InventoryItem o = new InventoryItem();
			o.Load(objJson, "");
			inventories.Add(o);
		}
		
		_buffs = new List<BuffDebuff>();
		objectsJson = MiniJSON.Json.Deserialize(data["_buffs"]) as List<object>;
		foreach(string objJson in objectsJson)
		{
			BuffDebuff o = new BuffDebuff();
			o.Load(objJson, "");
			_buffs.Add(o);
		}
		
		progressionSpeed = data["progressionSpeed"];
		
		extraInfo = new ExtendedCharacterStats();
		extraInfo.Load(data["extraInfo"], "");
		
		if(data.ContainsKey("nonLethalDamage"))
			nonLethalDamage = int.Parse(data["nonLethalDamage"] as string);
	}
	
	public override string ToJSON()
	{
		Dictionary<string,string> data = new Dictionary<string, string>();
		
		data.Add("name", name);
		data.Add("race", race.ToJSON());
		data.Add("_Strength", _Strength.ToString());
		data.Add("_Dexterity", _Dexterity.ToString());
		data.Add("_Constitution", _Constitution.ToString());
		data.Add("_Intelligence", _Intelligence.ToString());
		data.Add("_Wisdom", _Wisdom.ToString());
		data.Add("_Charisma", _Charisma.ToString());
		data.Add("Damage", Damage.ToString());
		data.Add("bonusHP", bonusHP.ToString());
		data.Add("Experience", Experience.ToString());
		data.Add("Gender", Gender.ToString());
		data.Add("Alignment", Alignment.ToString());
		data.Add("progressionSpeed", progressionSpeed.ToString());
		data.Add("extraInfo", extraInfo.ToJSON());
		
		data.Add("BonusFort", BonusFort.ToString());
		data.Add("BonusRef", BonusRef.ToString());
		data.Add("BonusWill", BonusWill.ToString());
		
		data.Add("classes", classes.ToJSON());
		data.Add("weapons", weapons.ToJSON());
		data.Add("skills", skills.ToJSON());
		data.Add("addedFeats", addedFeats.ToJSON());
		data.Add("addedSpecialAbilities", addedSpecialAbilities.ToJSON());
		data.Add("inventories", inventories.ToJSON());
		data.Add("_buffs", _buffs.ToJSON());
		
		data.Add("BonusSpeed", BonusSpeed.ToString());
		data.Add("nonLethalDamage", nonLethalDamage.ToString());
		
		data.Add("description", description);
		
		return MiniJSON.Json.Serialize(data);
	}
	
	public CharacterSheet DeepCopy()
	{
		CharacterSheet a = new CharacterSheet();
		a.Load(ToJSON(), fileName);
		return a;
	}
}
